/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "CommonJobFactory.h"
#include "nas_share/backup/HetroBackupJob.h"
#include "nas_index/BuildIndexServiceJob.h"
#include "nas_livemount/LivemountServiceJob.h"
#include "constant/PluginServiceCommon.h"
#include "nas_snapshot/backup/SnapshotBackup.h"
#include "nas_snapshot/delete/SnapshotDelete.h"
#include "nas_snapshot/restore/SnapshotRestore.h"
#include "nas_share/restore/HetroRestoreJob.h"
#include "nas_replication/backup/ReplicationBackupJob.h"
#include "nas_replication/restore/ReplicationRestoreJob.h"
#include "nas_archive/NasArchiveRestore.h"

using namespace std::placeholders;

namespace {
    constexpr auto MODULE = "CommonJobFactory";
    const int NUM_INT_1 = 1;
    const int NUM_INT_100 = 100;
    const std::string NAS_SHARE_STR = "NasShare";
    const std::string NAS_FILESYSTEM_STR = "NasFileSystem";
    constexpr auto FLR_RESTORE_TYPE_ORIGIN_VALUE = "original";
    const std::string LOCAL_RESTORE = "LocalRestore";
} // 本文件使用的变量

CommonJobFactory::CommonJobFactory()
{
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SHARE), JobType::BACKUP)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<HetroBackupJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SHARE), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<HetroRestoreJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_FLR), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<HetroRestoreJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_INDEX), JobType::INDEX)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<BuildIndexServiceJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SHARE), JobType::LIVEMOUNT)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<LivemountServiceJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_REPLICATION), JobType::LIVEMOUNT)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<LivemountServiceJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SNAPSHOT), JobType::BACKUP)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<SnapshotBackup>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SNAPSHOT), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<SnapshotRestore>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_SNAPSHOT), JobType::DELCOPY)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<SnapshotDelete>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_REPLICATION), JobType::BACKUP)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<ReplicationBackupJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_REPLICATION), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<ReplicationRestoreJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_ARCHIVE), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<NasArchiveRestore>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_FLR), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<HetroRestoreJob>, this, _1));
}

CommonJobFactory::~CommonJobFactory() {}

uint64_t CommonJobFactory::GetJobType(uint64_t appType, JobType jobType)
{
    return appType * NUM_INT_100 + static_cast<uint64_t>(jobType) * NUM_INT_1;
}


uint64_t CommonJobFactory::GetAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo, JobType jobType)
{
    if (jobType == JobType::BACKUP) {
        return GetBackupAppType(jobCommonInfo);
    }
    if (jobType == JobType::RESTORE) {
        return GetRestoreAppType(jobCommonInfo);
    }
    if (jobType == JobType::DELCOPY) {
        return GetDelCopyAppType(jobCommonInfo);
    }
    if (jobType == JobType::INDEX) {
        return GetIndexAppType(jobCommonInfo);
    }
    if (jobType == JobType::LIVEMOUNT) {
        return GetLivemountAppType(jobCommonInfo);
    }
    HCP_Log(ERR, MODULE) << "jobType is wrong ,the appType is: " << static_cast<int>(jobType) << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetBackupAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::BackupJob> jobInfo = std::dynamic_pointer_cast<AppProtect::BackupJob>(tempPtr);
    if (jobInfo == nullptr) {
        HCP_Log(ERR, MODULE) << "Pointer conversion failed., please check it! "<< HCPENDLOG;
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }
    if (jobInfo->jobParam.backupType == AppProtect::BackupJobType::SNAPSHOT) {
        return APPTYPE_SNAPSHOT;
    }
    std::string appTypeString = jobInfo->protectObject.subType;
    if (appTypeString == NAS_FILESYSTEM_STR) {
        return APPTYPE_REPLICATION;
    } else if (appTypeString == NAS_SHARE_STR) {
        return APPTYPE_SHARE;
    } else {
        return APPTYPE_NONE;
    }
    HCP_Log(ERR, MODULE) << "subJobType is wrong ,the appTypeString is: "<< appTypeString << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetDelCopyAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    return APPTYPE_SNAPSHOT;
}

uint64_t CommonJobFactory::GetRestoreAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::RestoreJob> jobInfo = std::dynamic_pointer_cast<AppProtect::RestoreJob>(tempPtr);
    if (jobInfo == nullptr) {
        HCP_Log(ERR, MODULE) << "Pointer conversion failed., please check it! "<< HCPENDLOG;
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }

    if (jobInfo->copies[0].dataType == AppProtect::CopyDataType::SNAPSHOT_COPY) {
        return APPTYPE_SNAPSHOT;
    }

    for (const AppProtect::Copy& copy : jobInfo->copies) {
        DBGLOG("Copy type: %d", static_cast<int>(copy.dataType));
        if (copy.dataType == AppProtect::CopyDataType::CLOUD_STORAGE_COPY) {
            return static_cast<uint64_t>(AppType::APPTYPE_ARCHIVE);
        } else if (copy.dataType == AppProtect::CopyDataType::TAPE_STORAGE_COPY) {
            // 磁带归档副本既支持直接恢复，也支持下载恢复，如果是下载恢复，走原流程
            if (jobInfo->jobParam.restoreMode == LOCAL_RESTORE) {
                break;
            }
            return static_cast<uint64_t>(AppType::APPTYPE_ARCHIVE);
        }
    }

    if (jobInfo->jobParam.restoreType == RestoreJobType::type::FINE_GRAINED_RESTORE) {
        return APPTYPE_FLR;
    } else if (jobInfo->targetObject.subType == NAS_FILESYSTEM_STR) {
        if (!jobInfo->copies.empty() && jobInfo->copies[0].dataType == AppProtect::CopyDataType::TAPE_STORAGE_COPY) {
            HCP_Log(INFO, MODULE) << "the copy type of NasFileSystem is tapearchive !" << HCPENDLOG;
            return APPTYPE_FLR;
        }
        FlrRestoreExtend m_flrExtend {};
        if (!Module::JsonHelper::JsonStringToStruct(jobInfo->extendInfo, m_flrExtend)) {
            HCP_Log(ERR, MODULE) << "convert flr extend info failed!" << HCPENDLOG;
            return APPTYPE_REPLICATION;
        }
        if (m_flrExtend.targetLocation == FLR_RESTORE_TYPE_ORIGIN_VALUE) {
            HCP_Log(INFO, MODULE) << "NasFileSystem is restored to origin location!" << HCPENDLOG;
            return APPTYPE_REPLICATION;
        }
        return APPTYPE_FLR;
    } else if (jobInfo->targetObject.subType == NAS_SHARE_STR) {
        if (!jobInfo->copies.empty() && jobInfo->copies[0].protectObject.subType == NAS_FILESYSTEM_STR) {
            return APPTYPE_FLR;
        }
        return APPTYPE_SHARE;
    } else {
        return APPTYPE_NONE;
    }
    HCP_Log(ERR, MODULE) << "subJobType is wrong ,the appTypeString is: "<< jobInfo->targetObject.subType << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetLivemountAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    DBGLOG("Enter GetLivemountAppType!");
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::LivemountJob> jobInfo = std::dynamic_pointer_cast<AppProtect::LivemountJob>(tempPtr);
    if (jobInfo == nullptr) {
        ERRLOG("Pointer conversion failed!");
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }
    std::string appTypeString = jobInfo->targetObject.subType;
    if (appTypeString == NAS_SHARE_STR) {
        return static_cast<uint64_t>(AppType::APPTYPE_SHARE);
    } else if (appTypeString == NAS_FILESYSTEM_STR) {
        return static_cast<uint64_t>(AppType::APPTYPE_REPLICATION);
    }
    HCP_Log(ERR, MODULE) << "subJobType is wrong ,the appTypeString is: "<< appTypeString << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetIndexAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    DBGLOG("Enter GetIndexAppType!");
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::BuildIndexJob> jobInfo =
        std::dynamic_pointer_cast<AppProtect::BuildIndexJob>(tempPtr);
    if (jobInfo == nullptr) {
        ERRLOG("Pointer conversion failed!");
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }
    std::string appTypeString = jobInfo->indexProtectObject.subType;
    if (appTypeString == NAS_SHARE_STR) {
        return static_cast<uint64_t>(AppType::APPTYPE_INDEX);
    } else if (appTypeString == NAS_FILESYSTEM_STR) {
        return static_cast<uint64_t>(AppType::APPTYPE_INDEX);
    }
    HCP_Log(ERR, MODULE) << "subJobType is wrong ,the appTypeString is: "<< appTypeString << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

std::shared_ptr<BasicJob> CommonJobFactory::CreateJob(
    const std::shared_ptr<JobCommonInfo>& jobCommonInfo, JobType jobType)
{
    HCP_Log(DEBUG, MODULE) << "Enter create job." << HCPENDLOG;
    uint64_t appType = GetAppType(jobCommonInfo, jobType);
    uint64_t mapKey = GetJobType(appType, jobType);
    if (m_commonJobMap.find(mapKey) == m_commonJobMap.end()) {
        HCP_Log(ERR, MODULE) << "no such operation in map, create job failed" << HCPENDLOG;
        HCP_Log(ERR, MODULE) << "appType is: " << appType <<
            "\tcommandType is: " << static_cast<int>(jobType)  << HCPENDLOG;
        return nullptr;
    }
    DBGLOG("Create job appType: %d, mapKet: %d", appType, mapKey);
    auto func = m_commonJobMap[mapKey];
    DBGLOG("Func: %s", typeid(func).name());
    auto jobPtr = func(jobCommonInfo, appType);
    if (jobPtr == nullptr) {
        HCP_Log(ERR, MODULE) << "create job failed" << HCPENDLOG;
        return nullptr;
    }
    HCP_Log(DEBUG, MODULE) << "Exit create job." << HCPENDLOG;
    return jobPtr;
}

template<typename T>
std::shared_ptr<BasicJob> CommonJobFactory::CreateFactoryJob(std::shared_ptr<JobCommonInfo> jobCommonInfo)
{
    HCP_Log(DEBUG, MODULE) << "Enter create factory job." << HCPENDLOG;
    auto job = std::make_shared<T>();
    job->SetJobInfo(jobCommonInfo);
    return job;
}